#!/usr/bin/python
#-*- coding: utf-8 -*-

# subversion info:
# $HeadURL: $
# $Author: $
# $Id: $
# $Revision: $

"""
  torrentparser: Parses .torrent files to extract its metadata.
  Authors = Rafael Treviño <skasi.7@gmail.es>
  Date = 24/10/09
"""

# Imports externals
import re
import os
import sys
try:
  import psyco # 2.5 times speed improvement
  psyco.full()
except ImportError:
  pass
import subprocess

# Imports internals
import Subdownloader
from Model import Episode, Event, DOWNLOADING, DONEWITHERROR, DONE

class Torrent(object):
  """
  Sphinx doc.
  """

  def __init__(self, filename):
    super(Torrent, self).__init__()
    self.__filename = filename
    self.file = None # Basename in $downloadPath directory
    self.__parse()

  def __repr__(self):
    return """< Torrent "%s" contains "%s" >""" % (self.__filename, self.file)

  def __parse(self):
    fd = open(self.__filename)
    info = self.__bdecode(fd.read()).get('info')
    fd.close()
    if info is None:
      raise RuntimeError, 'No torrent information present'
    if info.has_key('files'):
      raise RuntimeError, 'Multiple file torrent mode not supported'
    self.file = info.get('name')
    if not self.file:
      raise RuntimeError, 'Not filename present'

  def __bdecode(self, data):
    chunks = list(data)
    chunks.reverse()
    root = self.__bdecodeChunk(chunks)
    return root

  def __bdecodeChunk(self, chunks):
    item = chunks.pop()
    decimalPat = re.compile('\d')
    decimal = decimalPat.findall

    if item == 'd': # Dictionary
      item = chunks.pop()
      d = dict()
      while item != 'e': # End delimiter
        chunks.append(item)
        key = self.__bdecodeChunk(chunks)
        d[key] = self.__bdecodeChunk(chunks)
        item = chunks.pop()
      return d

    elif item == 'l': # List
      item = chunks.pop()
      l = list()
      while item != 'e':
        chunks.append(item)
        l.append(self.__bdecodeChunk(chunks))
        item = chunks.pop()
      return l

    elif item == 'i': # Integer
      item = chunks.pop()
      i = ''
      while item != 'e':
        i += item
        item = chunks.pop()
      return int(i)

    elif decimal(item): # Byte string
      d = ''
      while decimal(item):
        d += item
        item = chunks.pop()
      s = ''
      for i in xrange(int(d)):
        s += chunks.pop()
      return s

    raise RuntimeError, 'Invalid data'


class TorrentParser(object):

  def __init__(self, config): 
    super(TorrentParser, self).__init__()
    self.__config = config

  def syncDB(self):
    for episode in Episode.select(Episode.q.status == DOWNLOADING):
      torrentPath = os.path.join(self.__config.get('torrentspath'),
          os.path.basename(episode.torrentUri))
      try:
        torrent = Torrent(torrentPath)
      except IOError:
        Event(title='Error: active torrent not found',
            description='Torrent file %s not found, but torrent marked as'
              ' DOWNLOADING. Done with error.' % torrentPath,
            type=DONEWITHERROR,
            episode=episode
          )
        episode.status = DONEWITHERROR
        continue
      except RuntimeError, msg:
        Event(title='Error: active torrent runtime error',
            description='Torrent file %s runtime error "%s", but torrent'
              ' marked as DOWNLOADING. Done with error.' % (torrentPath, msg),
            type=DONEWITHERROR,
            episode=episode
          )
        episode.status = DONEWITHERROR
        continue
      if not os.access(os.path.join(self.__config.get('downloadspath'),
          torrent.file), os.R_OK):
        continue # Already not downloaded
      
      downloadPath = os.path.join(self.__config.get('downloadspath'),
        torrent.file)
      finalPath = os.path.join(self.__config.get('seriesrootpath'),
          episode.season.tvSerie.name, 'Season%d' % episode.season.number,
          '%s.S%02dE%02d%s%s' % (episode.season.tvSerie.name,
            episode.season.number, episode.number, episode.title and '.%s' %
            episode.title or '', os.path.splitext(torrent.file)[1]))

      if os.access(self.__config.get('seriesrootpath'),
          os.R_OK | os.W_OK):
        params = ['mkdir', '-p', os.path.dirname(finalPath)]
        print >> sys.stderr, 'Running "%s"' % ' '.join(params)
        retCode = subprocess.call(params)
        print >> sys.stderr, 'mkdir returned %d on exit' % retCode
      params = ['mv', downloadPath, finalPath]
      print >> sys.stderr, 'Running "%s"' % ' '.join(params)
      retCode = subprocess.call(params)
      print >> sys.stderr, 'mv returned %d on exit' % retCode
      if retCode:
        Event(title='Error: finished download stuck',
            description='Unable to move finished download %s.'
            ' Done with error.' % downloadPath,
            type=DONEWITHERROR,
            episode=episode
          )
        episode.status = DONEWITHERROR
        continue

      # When moved torrent file is no longer useful
      os.unlink(torrentPath)

      if self.__config.get('sublanguage'):
        subStatus = Subdownloader.download(finalPath,
          self.__config.get('sublanguage'))
      else:
        subStatus = None
      if subStatus:
        Event(title='Error: unable to find subtitle',
            description='Unable to find subtitle for %s.'
            ' Done with error.' % finalPath,
            type=DONEWITHERROR,
            episode=episode
          )
        episode.status = DONEWITHERROR
      else:
        Event(title='Download finished %s' % finalPath,
            description='Download %s is ready to be seen. Done.' %
              finalPath,
            type=DONE,
            episode=episode
          )
        episode.status = DONE


# Main entry point
if __name__ == '__main__':
  # TODO: Better logging and options
  try:
    torrent = os.path.abspath(sys.argv[1])
  except IndexError:
    print >> sys.stderr, 'Usage: %s <torrent file>' % sys.argv[0]
    sys.exit(1)
  if torrent:
    print >> sys.stderr, 'Using torrent filename "%s"' % torrent
  else:
    print >> sys.stderr, 'Unable to determine the torrent filename'
    sys.exit(1)

  print >> sys.stderr, Torrent(torrent)

